#include "ReflectableWidgetAttribute.h"

#include "Containers/Any.h"

#include "Reflection/Function.h"
#include "Object/Object.h"


class CWidgetAttributeScriptFunctionValueGetter : public TWidgetAttributeValueGetterBase
{
    TWeakObjectPtr<RObject> Object;
    CFunction* Function;

public:
    CWidgetAttributeScriptFunctionValueGetter(TWeakObjectPtr<RObject> InObject, CFunction* InFunction)
        :Object(InObject)
        ,Function(InFunction)
    {
    }

    virtual bool IsValid() { return Object.IsValid(); }

    virtual void GetValuePointer(void* OutValue) override
    {
        assert (Object.IsValid());

        CAny ReturnValue = Function->Invoke({ Object.Lock() });

        ReturnValue.GetType()->CloneInstance(ReturnValue.GetPointer(),OutValue);
    }
};



class CWidgetAttributeScriptDelegateValueGetter : public TWidgetAttributeValueGetterBase
{
    SScriptDelegateBase Delegate;
public:
    CWidgetAttributeScriptDelegateValueGetter(const SScriptDelegateBase& InDelegate)
        :Delegate(InDelegate)
    {
    }

    virtual bool IsValid() { return Delegate.IsBound(); }

    virtual void GetValuePointer(void* OutValue) override
    {
        assert (Delegate.IsBound());

        CAny ReturnValue = Delegate.ScriptInvoke({});

        ReturnValue.GetType()->CloneInstance(ReturnValue.GetPointer(),OutValue);
    }

};



CReflectbleWidgetAttribute::CReflectbleWidgetAttribute(const CReflectbleWidgetAttribute &Other)
{
    ValueGetter= Other.ValueGetter;
}



void CReflectbleWidgetAttribute::SetValue(CType *InDataType, const void *InValue)
{
    class CScriptValueGetter : public TWidgetAttributeValueGetterBase
    {
    public:
        CAny Value;
        
        CScriptValueGetter(const CAny& InValue)
            :Value(InValue)
        {
        }

        virtual void GetValuePointer(void* OutValue) override
        {
            Value.GetType()->CloneInstance(Value.GetPointer(),OutValue);
        }
    };

    void * ValuePtr= malloc(InDataType->GetSize());
    InDataType->CloneInstance(InValue,ValuePtr);
    ValueGetter= std::make_shared<CScriptValueGetter>(CAny(InDataType,ValuePtr));
}

void CReflectbleWidgetAttribute::SetFunction(TSharedObjectPtr<RObject> InObject, CFunction *InFunction)
{
    ValueGetter= std::make_shared<CWidgetAttributeScriptFunctionValueGetter>(InObject,InFunction);
}

void CReflectbleWidgetAttribute::SetDelegate(const SScriptDelegateBase &InDelegate)
{
    ValueGetter= std::make_shared<CWidgetAttributeScriptDelegateValueGetter>(InDelegate);
}

void* CReflectbleWidgetAttribute::GetValue(CType *InDataType) const
{
    if(!ValueGetter  ||  !ValueGetter->IsValid())
    {
        return InDataType->CreateInstance();
    }

    void* ReturnValue= malloc(InDataType->GetSize());
    ValueGetter->GetValuePointer(ReturnValue);
    return ReturnValue;
}

uint64_t CReflectbleWidgetAttribute::GetHashCode() const
{
    return  WH::GetHashCode(ValueGetter);
}
